﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Athena.Runtime.Client.RouteAddressOverride.Polling
{
    using HealthChecks;
    using RouteAddress;
    using RouteAddress.Implementations;
    public class PollingRouteSelector : BaseRouteSelector
    {
        private readonly IHealthCheckDispatch _healthCheckDispatch;
        private readonly ConcurrentDictionary<string, Lazy<PollingAddressEntry>> _concurrentAddressEntry;

        public PollingRouteSelector(IHealthCheckDispatch healthCheckDispatch)
        {
            _healthCheckDispatch = healthCheckDispatch;
            _concurrentAddressEntry = new ConcurrentDictionary<string, Lazy<PollingAddressEntry>>();
        }

        protected override async ValueTask<Route> SelectAsync(ServiceRoute serviceRoute)
        {
            var key = serviceRoute.CorrelationId;
            var addressEntry = _concurrentAddressEntry.GetOrAdd(key, x => new Lazy<PollingAddressEntry>(() => new PollingAddressEntry(serviceRoute.Routes))).Value;

            Route route;
            do
            {
                route = addressEntry.GetAddress();
            } while (!await _healthCheckDispatch.IsHealth(route));
            return route;
        }

        public class PollingAddressEntry
        {
            private readonly Route[] _routes;
            private int _maxIndex;
            private int _index;
            private int _lock;

            public PollingAddressEntry(IEnumerable<Route> routes)
            {
                _routes = routes.ToArray();
                _maxIndex = _routes.Length - 1;
            }

            public Route GetAddress()
            {
                while (true)
                {
                    //如果无法得到锁则等待
                    if (Interlocked.Exchange(ref _lock, 1) != 0)
                    {
                        default(SpinWait).SpinOnce();
                        continue;
                    }

                    var address = _routes[_index];

                    //设置为下一个
                    if (_maxIndex > _index)
                        _index++;
                    else
                        _index = 0;

                    //释放锁
                    Interlocked.Exchange(ref _lock, 0);

                    return address;
                }
            }
        }
    }
}
